#include "CSession.h"


CSession::CSession(boost::asio::io_context& ioc, CServer* server):_ioc(ioc),_server(server),_socket(_ioc),_b_stop(false)
{
	boost::uuids::uuid uuid=boost::uuids::random_generator()();
	_uuid = boost::uuids::to_string(uuid);
	_head_node = std::make_shared<MsgNode>(HEAD_TOTAL_LEN);
	std::cout << "Session construct" << std::endl;
}

boost::asio::ip::tcp::socket& CSession::getSocket()
{
	return _socket;
}

void CSession::start()
{
	auto shared_this = shared_from_this();
	boost::asio::co_spawn(_ioc, [shared_this,this]() ->boost::asio::awaitable<void> {
		try {
			while (!_b_stop) {
				_head_node->Clear();
				size_t n =co_await boost::asio::async_read(_socket, boost::asio::buffer(_head_node->_data, HEAD_TOTAL_LEN), 
					boost::asio::use_awaitable);
				if (n == 0) {
					HandleErr("Peer Closed");
					co_return;
				}
				short msg_id;
				short msg_len;
				memcpy(&msg_id, _head_node->_data, HEAD_ID_LEN);
				memcpy(&msg_len, _head_node->_data + HEAD_ID_LEN, HEAD_MSG_LEN);
				msg_id = boost::asio::detail::socket_ops::network_to_host_short(msg_id);
				msg_len = boost::asio::detail::socket_ops::network_to_host_short(msg_len);
				if (msg_id > MAX_LENGTH||msg_len>MAX_LENGTH) {
					HandleErr("Invalid Id or Len");
					co_return;
				}
				_recv_node = std::make_shared<RecvNode>(msg_len, msg_id);

				n=co_await boost::asio::async_read(_socket, boost::asio::buffer(_recv_node->_data, _recv_node->_total_size), 
					boost::asio::use_awaitable);
				if (n == 0) {
					HandleErr("Peer Closed");
					co_return;
				}
				std::cout << "recv data is: " << std::string(_recv_node->_data, _recv_node->_total_size)<<"len is "<<_recv_node->_total_size << std::endl;
				LogicSys::getInst().PostMsgToQue(std::make_shared<LogicNode>(shared_from_this(), _recv_node));
			}
		}
		catch (std::exception& e) {
			std::cerr << "CSession start Err:" << e.what() << std::endl;
			Close();
			_server->ClearSession(_uuid);
		}

		},boost::asio::detached);
}

std::string& CSession::getuuid()
{
	return _uuid;
}

void CSession::Close()
{
	_socket.close();
	_b_stop = true;
}

void CSession::HandleErr(std::string err) {
	std::cout << err << std::endl;
	Close();
	_server->ClearSession(_uuid);
}

CSession::~CSession()
{
	try {
		std::cout << "~CSession()" << std::endl;
		Close();
	}
	catch (std::exception& e) {
		std::cout << "Exception in ~CSession():" << e.what() << std::endl;
	}
	
}

void CSession::Send(std::string msg, short msg_id)
{
	Send(msg.c_str(), msg.size(), msg_id);
}

void CSession::Send(const char* msg, short msg_len,short msg_id)
{
	std::unique_lock<std::mutex> lock(_send_mtx);
	size_t send_que_size = _send_que.size();
	if (send_que_size > MAX_SENDQUE) {
		std::cout << "Session :" << _uuid << " Queue is full" << std::endl;
		return;
	}
	_send_que.push(std::make_shared<SendNode>(msg, msg_len, msg_id));
	if (send_que_size > 0) {
		return;
	}
	auto& msgnode = _send_que.front();
	lock.unlock();
	boost::asio::async_write(_socket, boost::asio::buffer(msgnode->_data, msgnode->_total_size), std::bind(&CSession::HandleWrite, this, std::placeholders::_1, shared_from_this()));
}

void CSession::HandleWrite(const boost::system::error_code& ec, std::shared_ptr<CSession> shared_self)
{
	try {
		if (!ec) {
			std::unique_lock<std::mutex> lock(_send_mtx);
			_send_que.pop();
			if (!_send_que.empty()) {
				auto& msgnode = _send_que.front();
				lock.unlock();
				boost::asio::async_write(_socket, boost::asio::buffer(msgnode->_data, msgnode->_total_size), 
					std::bind(&CSession::HandleWrite, this, std::placeholders::_1, shared_self));
			}
		}
		else {
			HandleErr("HandleWrite err");
		}
	}
	catch (std::exception& e) {
		std::cout << "Exception HandleWrite err:" << e.what() << std::endl;
		Close();
		_server->ClearSession(_uuid);
	}

}

LogicNode::LogicNode(std::shared_ptr<CSession> CSession, std::shared_ptr<RecvNode> RecvNode):_session(CSession),_recvnode(RecvNode)
{

}
